
/**
 * MyVue   框架构造函数
 * Observe 执行数据响应式（分辨数据是对象还是数组）
 * Compile 编译模板，初始化视图，收集依赖 （更新函数，watcher 创建）
 * Watcher 执行更新函数（更新dom）
 * Dep     管理多个Watcher ，批量更新
 */

/* 
   数组响应式
  1.  找到数组原型
  2.  覆盖那些能够修改数组的更新方法，使其可以通知更新
  3.  将新的原型设置到数组的实例（_proto_） 上
*/
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

const orginalProto = Array.prototype
const arrprototype = Object.create(orginalProto)

methodsToPatch.forEach(method => {
  arrprototype[method] = function () {

    orginalProto[method].apply(this, arguments)
    //  覆盖
    console.log('数组' + method + '执行')

  }
})


function defineReactive (obj, key, val) {
  // 递归
  observer(val)

  // 创建 Dep 实例 
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    get () {
      console.log('拦截数据get')
      // console.log(Dep.target);
      // 依赖收集 
      Dep.target && dep.addDep(Dep.target)

      return val
    },
    set (newValue) {
      if (newValue !== val) {
        console.log('更新数据set')
        observer(val)
        val = newValue

        //  触发更新
        dep.notify()
      }
    }
  })
}

function observer (obj) {
  if (typeof obj !== "object" || obj === null) {
    return
  }
  new Observer(obj)
}

function proxy (vm) {
  Object.keys(vm.$data).forEach(key => {
    Object.defineProperty(vm, key, {
      get () {
        return vm.$data[key]
      },
      set (val) {
        vm.$data[key] = val
      }
    })
  })
}

class Compile {
  constructor(vm, el) {
    this.$vm = vm
    const dom = document.querySelector(el)

    if (dom) {
      this.compile(dom)
    }
  }

  compile (dom) {
    const childNodes = dom.childNodes

    childNodes.forEach(node => {
      switch (node.nodeType) {
        case 1:
          this.elementText(node)
          break;
        case 3:
          if (this.isInner(node)) {
            this.compileText(node)
          }
          break;
        default:
          break;
      }
    })
  }

  elementText (node) {
    const attres = node.attributes
    Array.from(attres).forEach(attre => {
      const attreName = attre.name
      const exp = attre.value
      // 指令 my-xxx
      if (this.isDir(attreName)) {
        const dir = attreName.substring(3)
        this[dir] && this[dir](node, exp)
      }
      // 事件 @Click
      if (this.isEvent(attreName)) {
        const dir = attreName.substring(1)
        // 事件监听
        this.eventHandler(node, exp, dir)
      }

    })

    if (node.childNodes.length > 0) {
      this.compile(node);
    }
  }
  //事件处理
  eventHandler (node, exp, dir) {

    const fn = this.$vm.$options.methods && this.$vm.$options.methods[exp]

    node.addEventListener(dir, fn.bind(this.$vm))
  }

  compileText (node) {
    this.update(node, RegExp.$1, 'text')
  }

  update (node, exp, dir) {
    //1.初始化
    const fn = this[dir + 'Updater']
    fn && fn(node, this.$vm[exp])

    // 2. 更新
    new Watcher(this.$vm, exp, (val) => {
      fn && fn(node, val)
    })
  }

  text (node, exp) {
    this.update(node, exp, 'text')
  }
  textUpdater (node, value) {
    node.textContent = value
  }

  html (node, exp) {
    this.update(node, exp, 'html')
  }
  htmlUpdater (node, value) {
    node.innerHTML = value
  }

  model (node, exp) {
    this.update(node, exp, 'model')

    //  事件监听
    node.addEventListener('input', e => {
      this.$vm[exp] = e.target.value
    })
  }
  modelUpdater (node, value) {
    node.value = value
  }

  // 判断是否 {{}}
  isInner (node) {
    return /\{\{(.*)\}\}/.test(node.textContent)
  }
  // 判断是否 my-xxx
  isDir (attre) {
    return attre.startsWith('my-')
  }
  // 判断是否 @xxx
  isEvent (attre) {
    return attre.indexOf('@') == 0
  }
}

class Watcher {
  constructor(vm, key, updateFn) {
    this.vm = vm
    this.key = key
    this.updateFn = updateFn

    //  触发依赖收集
    Dep.target = this
    this.vm[this.key]
    Dep.target = null
  }

  // 未来 Dep 会调用
  update () {
    this.updateFn.call(this.vm, this.vm[this.key])
  }
}

class Dep {
  constructor() {
    this.deps = []
  }

  addDep (dep) {
    this.deps.push(dep)
  }


  notify () {
    this.deps.forEach(dep => {
      dep.update()
    })
  }
}

class Observer {
  constructor(value) {
    this.value = value
    if (this.isArray(value)) {
      //  数组的响应式处理
      this.Array(value)
    } else {
      //  对象的响应式处理
      this.walk(value)
    }
  }


  walk (obj) {
    Object.keys(obj).forEach(key => {
      defineReactive(obj, key, obj[key])
    })
  }

  Array (obj) {
    //  覆盖原型 替换操作
    // obj._proto_ = arrprototype
    // // 对数组的原型 执行响应式
    // const keys = Object.keys(obj)
    // console.log(obj);
    // for (let i = 0; i < obj.length; i++) {
    //   observer(obj[i])
    // }
  }

  isArray (value) {
    return Array.isArray(value)
  }
}

class MyVue {
  constructor(options) {
    this.$options = options
    this.$data = options.data

    //响应式数据处理
    observer(this.$data)
    // 代理  
    proxy(this)
    //模板
    new Compile(this, options.el)
  }

}